package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import com.hmdp.entity.Voucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherMapper;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.SimpleRedisLock;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
@Slf4j
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
    @Autowired
    private VoucherOrderMapper voucherOrderMapper;
    @Autowired
    private VoucherMapper voucherMapper;
    @Autowired
    RedisIdWorker redisIdWorker;
    //注入自己，防止事务失效
    @Autowired
     VoucherOrderServiceImpl voucherOrderService;
    @Autowired
    SimpleRedisLock  simpleRedisLock;
    @Autowired
    RedisTemplate redisTemplate;

    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
    static {
        SECKILL_SCRIPT=new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }
    //异步处理线程池
    private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();

    //在类初始化之后执行，因为当这个类初始化好了之后，随时都是有可能要执行的
    @PostConstruct
    private void init() {
        log.info("---------------线程池初始化-----------------");
        SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());
    }

    @Transactional
    private class  VoucherOrderHandler implements Runnable{
        @Override
        public void run() {
            while (true)
            {
                //获取消息队列中的订单信息
                log.info("消息队列");
                List list=null;
                try{
                 list = redisTemplate.opsForStream().read(
                        Consumer.from("g1", "c1"),
                        StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),
                        StreamOffset.create("stream.orders", ReadOffset.lastConsumed())
                );
                }catch (Exception e){
                    log.info("从消息队列里读取订单错误");
                }

                log.info("list:{}",list);
                //判断订单信息是否为空
                if(list==null||list.isEmpty())
                {
                    try {
                        Thread.sleep(2000L);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    //如果为空说明没有信息，继续循环
                    continue;
                }
                //解析数据
                MapRecord<String,Object,Object> record = (MapRecord<String,Object,Object>)list.get(0);
                Map<Object, Object> value = record.getValue();
                VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);
                try {
                    //创建订单
                    log.info("创建订单");
                    voucherOrderService.createVoucherOrder(voucherOrder);
                    //确认消息
                    redisTemplate.opsForStream().acknowledge("stream.orders","g1",record.getId());
                }
                catch (Exception e)
                {
                    log.error(e.getMessage(),e);
                    e.printStackTrace();
                    handlePendingList();
                }
            }
        }
    }
    private void handlePendingList() {
        while (true) {
            try {
                // 1.获取pending-list中的订单信息 XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS s1 0
                List<MapRecord<String, Object, Object>> list = redisTemplate.opsForStream().read(
                        Consumer.from("g1", "c1"),
                        StreamReadOptions.empty().count(1),
                        StreamOffset.create("stream.orders", ReadOffset.from("0"))
                );
                // 2.判断订单信息是否为空
                if (list == null || list.isEmpty()) {
                    // 如果为null，说明没有异常消息，结束循环
                    break;
                }
                // 解析数据
                MapRecord<String, Object, Object> record = list.get(0);
                Map<Object, Object> value = record.getValue();
                VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);
                // 3.创建订单
                voucherOrderService.createVoucherOrder(voucherOrder);
                // 4.确认消息 XACK
                redisTemplate.opsForStream().acknowledge("stream.orders", "g1", record.getId());
            } catch (Exception e) {
                log.error("处理pendding订单异常", e);
                try{
                    Thread.sleep(20);
                }catch(Exception ee){
                    ee.printStackTrace();
                }
            }
        }
    }


    @Override
    public  Result buyVoucher(Long voucherId) {
        long orderId = redisIdWorker.nextId("order");
        Long id = UserHolder.getUser().getId();
        //执行lua脚本
        try {
            Long result = (Long)redisTemplate.execute(
                    SECKILL_SCRIPT,
                    Collections.emptyList(),
                    voucherId.toString(),
                    id.toString(),
                    String.valueOf(orderId)
            );
            int r=result.intValue();
            if(r!=0)
            {
                return Result.fail(r==1?"库存不足":"不能重复下单");
            }
        }
        catch (Exception e)
        {
            log.error(e.getMessage(),e);
        }

        return Result.ok(orderId);


//        UserDTO user = UserHolder.getUser();
//        //根据ID查询优惠券
//        Voucher voucher = voucherMapper.selectSeckillVoucherById(voucherId);
//        if (LocalDateTime.now().isBefore(voucher.getBeginTime())) {
//            return Result.fail("秒杀未开始");
//        }
//        if (LocalDateTime.now().isAfter(voucher.getEndTime())) {
//            return Result.fail("秒杀已结束");
//        }
//        if (voucher.getStock() < 1) {
//            return Result.fail("库存不足");
//        }
//        //查询是否有重复订单
//        //加锁，保证一个用户只能有一个线程来查询
////        synchronized (user.getId().toString().intern()) {
////            return voucherOrderService.getResult(voucherId, user);
////        }
//
//        //分布式锁
//        try{
//            boolean b = simpleRedisLock.tyrLock("order:" + user.getId(), 10, TimeUnit.SECONDS);
//            if (!b) {
//                return Result.fail("不允许重复下单");
//            }
//            return voucherOrderService.getResult(voucherId,user);
//        }
//        finally {
//            simpleRedisLock.unLock("order:"+user.getId());
//        }

    }
    @Transactional
    @Override
    public void createVoucherOrder(VoucherOrder voucherOrder) {
        //插入订单
        log.info("插入订单");
        voucherOrderMapper.insert(voucherOrder);

        //扣减库存
        log.info("扣减库存");
        voucherMapper.updateStock(voucherOrder.getVoucherId());
    }

    @Transactional
    public Result getResult(Long voucherId, UserDTO user) {
        int count = voucherOrderMapper.selectSameOrder(user.getId(), voucherId);
        if (count > 0) {
            return Result.fail("该用户已经下过单");
        }
        //将sql语句改成 where stock>0 可解决超卖问题，类似与乐观锁
        int row = voucherMapper.updateStock(voucherId);
        if (row != 1) {
            return Result.fail("库存不足");
        }
        long orderId = redisIdWorker.nextId("order");
        VoucherOrder voucherOrder = new VoucherOrder();
        voucherOrder.setId(orderId);
        Long userId = UserHolder.getUser().getId();
        voucherOrder.setUserId(userId);
        voucherOrder.setVoucherId(voucherId);
        voucherOrderMapper.insert(voucherOrder);
        return Result.ok(orderId);
    }
}
